(*
     The contents of this file are subject to the Mozilla Public License
     Version 1.1 (the "License"); you may not use this file except in
     compliance with the License. You may obtain a copy of the License at
     http://www.mozilla.org/MPL/

     Software distributed under the License is distributed on an "AS IS"
     basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the
     License for the specific language governing rights and limitations
     under the License.

     The Original Code is CrossFPC.

     The Initial Developer of the Original Code is Simon Kissel.
     Portions created by Benjamin Rosseaux are Copyright (C) Benjamin Rosseaux.
     All Rights Reserved.

*)

unit cfcompileroptions;

interface

uses
  cfpcutils;

type
  TSyntaxOption = (soCStyle, soAsserts, soLabelGoto, soCppInline, soFPCylix,
                   soCMakros, soInitDone, soStatic,  soAnsi, SoNoUnitNameCheck,
                   soPackSet);
  TSyntaxOptions = set of TSyntaxOption;
  TGenOption =(goEmulateFP, goGeneratePIC, goIOChecking, goOmitLinkingStage,
               goOverflowCheck, goRangeCheck, goStackChecking, goSmartlinkedLib,
               goSmartLinking, goStripSymbols, goSmallerCode);
  TOptimizer = (oNoOptimize, oOptimize1, oOptimize2, oOptimize3);
  TFPCDialect = (fdDefault, fdObjFPC, fdDelphi, fdTP, fdGPC, fdMacPas);

  TGenOptions = set of TGenOption;

  TProjectOptions = class
  private
    fSyntaxOptions: TSyntaxOptions;
    fGenOptions: TGenOptions;
    fOptimizer: TOptimizer;
    fFPCDialect: TFPCDialect;
    fOutPutDir: string;
    fProjectFile: string;
    fCustomParameters: string;
    fDebuggerArguments: string;
    procedure SetProject(Value: string);
  protected
    function LoadConfig(FileName: String): Boolean;
    function SaveConfig(FileName: String): Boolean;
  public
    constructor Create;
    destructor Destroy; override;
    function GetConfigString: string;
    procedure Clear;
  published
    property SyntaxOptions: TSyntaxOptions read fSyntaxOptions write fSyntaxOptions;
    property CodeGenOptions: TGenOptions read fGenOptions write fGenOptions;
    property Optimizer: TOptimizer read fOptimizer write fOptimizer;
    property FPCDialect: TFPCDialect read fFPCDialect write fFPCDialect;
    property ProjectFile: string read fProjectFile write SetProject;
    property OutPutDir: string read fOutPutDir write fOutPutDir;
    property CustomParameters: string read fCustomParameters write fCustomParameters;
    property DebuggerArguments: string read fDebuggerArguments write fDebuggerArguments;
  end;

implementation

uses SysUtils, Dialogs, IniFiles, BeRoAppDataPath;

const SFPCDialect: array[TFPCDialect] of string = ('-Mfpc','-Mobjfpc',
                                          '-Mdelphi','-Mtp','-Mgpc','-Mmacpas');
      SOptimizer: array[TOptimizer] of string = ('-O-','-O1','-O2','-O3');
      SSyntaxOptions: array[TSyntaxOption] of string = ('-Sc','-Sa','-Sg','-Si',
      '-Sk','-Sm','-Ss','-St','-Sh','-Un','-CPPackset=1');
      SGenOptions: array[TGenOption] of string = ('-Ce','-Cg','-Ci','-Cn','-Co',
      '-Cr','-Ct','-CX','-XX', '-Xs', '-Os');

{ TProjectOptions }

procedure TProjectOptions.Clear;
begin
  fSyntaxOptions:=[soAnsi, soPackSet];
  fGenOptions:=[goStripSymbols];
  fOptimizer:=oOptimize2;
  fFPCDialect:=fdDelphi;
  fOutPutDir:='';
end;

constructor TProjectOptions.Create;
begin
  if not LoadConfig(IncludeTrailingPathDelimiter(String(GetAppDataPath('CrossFPC')))+'default.cfp') then
  begin
    Clear;
    SaveConfig(IncludeTrailingPathDelimiter(String(GetAppDataPath('CrossFPC')))+'default.cfp');
  end;
end;

destructor TProjectOptions.Destroy;
begin
  if fProjectFile<>'' then
  SaveConfig(RemoveFileExt(fProjectFile)+'.cfp') else
  SaveConfig(IncludeTrailingPathDelimiter(String(GetAppDataPath('CrossFPC')))+'default.cfp');

  inherited;
end;

function TProjectOptions.GetConfigString: string;
var SS: TSyntaxOption;
    GS: TGenOption;
begin
  case FPCDialect of
    fdDefault: result:='-Mfpc';
    fdObjFPC: result:='-Mobjfpc';
    fdDelphi: result:='-Mdelphi';
    fdTP: result:='-Mtp';
    fdGPC: result:='-Mgpc';
    fdMacPas: result:='-Mmacpas';
   else result:='';
  end;

  for SS:=Low(TSyntaxOption) to High(TSyntaxOption) do
  if SS in SyntaxOptions then result:=result+' '+sSyntaxOptions[SS];

  for GS:=Low(TGenOption) to High(TGenOption) do
  if GS in CodeGenOptions then result:=result+' '+sGenOptions[GS];

  case Optimizer of
    oNoOptimize:result:=result+' -O-';
    oOptimize1: result:=result+' -O1';
    oOptimize2: result:=result+' -O2';
    oOptimize3: result:=result+' -O3';
  end;
  result:=CombineFlags(fCustomParameters, result);
end;

function TProjectOptions.LoadConfig(FileName: String): Boolean;
var Ini: TIniFile;
    i,j: Integer;
    SS: TSyntaxOption;
    GS: TGenOption;
begin
  if not FileExists(FileName) then
  begin
    result:=False;
    Exit;
  end
  else result:=True;

  Ini:=TIniFile.Create(FileName);

  try
    j:=Ini.ReadInteger('Settings','Syntax', 0);
    fSyntaxOptions:=[]; i:=0;
    for SS:=Low(TSyntaxOption) to High(TSyntaxOption) do
    begin
      if (j and (1 shl i)=(1 shl i)) then
      fSyntaxOptions:=fSyntaxOptions+[SS];
      inc(i);
    end;

    j:=Ini.ReadInteger('Settings','CodeGen', 0);
    fGenOptions:=[]; i:=0;
    for GS:=Low(TGenOption) to High(TGenOption) do
    begin
      if (j and (1 shl i)=(1 shl i)) then
      fGenOptions:=fGenOptions+[GS];
      inc(i);
    end;

    fOptimizer:=TOptimizer(Ini.ReadInteger('Settings','Optimizer', 0));
    fFPCDialect:=TFPCDialect(Ini.ReadInteger('Settings','Dialect', 0));

    fDebuggerArguments:=Ini.ReadString('Settings','DebuggerArguments', '--args {binary}');
  except
    result:=False;
  end;
  Ini.Free;

end;

function TProjectOptions.SaveConfig(FileName: String): Boolean;
var Ini: TIniFile;
    i,j: Integer;
    SS: TSyntaxOption;
    OS: TGenOption;
begin
  result:=True;
  try
    Ini:=TIniFile.Create(FileName);
    try

      i:=0; j:=0;
      for SS:=Low(TSyntaxOption) to High(TSyntaxOption) do
      begin
        if SS in fSyntaxOptions then j:=j+(1 shl i);
        inc(i);
      end;
      Ini.WriteInteger('Settings','Syntax',j);

      i:=0; j:=0;
      for OS:=Low(TGenOption) to High(TGenOption) do
      begin
        if OS in fGenOptions then j:=j+(1 shl i);
        inc(i);
      end;
      Ini.WriteInteger('Settings','CodeGen',j);
      Ini.WriteInteger('Settings','Optimizer', Integer(fOptimizer));
      Ini.WriteInteger('Settings','Dialect', Integer(fFPCDialect));
      Ini.WriteString('Settings','Parameters', fCustomParameters);
      Ini.WriteString('Settings','DebuggerArguments', fDebuggerArguments);
    finally
     Ini.Free;
    end;
  except
    result:=False;
  end;
end;

procedure TProjectOptions.SetProject(Value: string);
begin
  if fProjectFile<>'' then
  begin
    SaveConfig(RemoveFileExt(fProjectFile)+'.cfp');
  end else
  begin
    SaveConfig(IncludeTrailingPathDelimiter(String(GetAppDataPath('CrossFPC')))+'default.cfp');
  end;

  if fProjectFile=Value then exit;
  fProjectFile:=Value;

  if fProjectFile='' then
  begin
    LoadConfig(IncludeTrailingPathDelimiter(String(GetAppDataPath('CrossFPC')))+'default.cfp');
    exit;
  end;

  if not LoadConfig(RemoveFileExt(fProjectFile)+'.cfp') then
  begin
    LoadConfig(IncludeTrailingPathDelimiter(String(GetAppDataPath('CrossFPC')))+'default.cfp');
    SaveConfig(RemoveFileExt(fProjectFile)+'.cfp');
  end;
end;

end.

